
//
//  SegmentDownload.m
//  SegmentDownloadDemo
//
//  Created by gao on 2018/5/29.
//  Copyright © 2018年 gao. All rights reserved.
//

#import "SegmentDownload.h"
#import "SegmentDownloadTask.h"
#import "SegmentDownloader.h"
#import "SegmentDownloadSession.h"
#import <UIKit/UIKit.h>
@interface SegmentDownload()
@property(nonatomic,assign)NSInteger maxConcurrent;
@property(nonatomic,strong)NSMutableArray<SegmentDownloadTask*> *downloadTaskArray;
@property(nonatomic,strong)NSMutableDictionary< NSString*,SegmentDownloadTask*>*downloadTaskDic;
@property(nonatomic,strong)NSMutableArray<SegmentDownloader*> *downloaders;
@property(nonatomic,strong)NSMutableArray<id<SegmentDownloadProtocol>>*receivers;

@property(nonatomic,assign)id<SegmentDownloadProtocol>containerReceiver;
/**
 是否可以下载 当调用startDownload 的时候,其值为YES
 */
@property(nonatomic,assign)BOOL canDownload;
@end

@implementation SegmentDownload
+(instancetype)share
{
    static SegmentDownload * _shareInstance = nil;
    
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _shareInstance = [[self alloc]init];
    });
    
    
    return _shareInstance;
}
-(instancetype)init{
    if (self = [super init]) {
        self.maxConcurrent = 1;
        self.downloaders = [NSMutableArray new];
        self.receivers = [NSMutableArray new];
        [self initDownlaodData];
    }
    return self;
}
-(void)initDownlaodData{
    NSString *downloadTaskPath = [SegmentDownload getArchiverPath];
    self.downloadTaskArray = [NSKeyedUnarchiver unarchiveObjectWithFile:downloadTaskPath];
    if (!self.downloadTaskArray) {
        self.downloadTaskArray = [NSMutableArray new];
        self.downloadTaskDic = [NSMutableDictionary new];
    }
    else{
        [self.downloadTaskArray enumerateObjectsUsingBlock:^(SegmentDownloadTask * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            [self.downloadTaskDic setValue:obj forKey:obj.downloadURL.absoluteString];
        }];
    }
}
+(void)saveDownloadData{
    SegmentDownload *manager = [SegmentDownload share];
    NSLock *lock = [[NSLock alloc] init];
    [lock lock];
    [NSKeyedArchiver archiveRootObject:manager.downloadTaskArray toFile:[SegmentDownload getArchiverPath]];
    [lock unlock];
}
+(void)clean{
    SegmentDownload *manager = [SegmentDownload share];
    manager.downloadTaskArray = [NSMutableArray new];
    manager.downloaders = [NSMutableArray new];
}
+(void)cleanDownloadSubTaskDownloadTask{
    SegmentDownload *manager = [SegmentDownload share];
    [manager.downloadTaskArray enumerateObjectsUsingBlock:^(SegmentDownloadTask * _Nonnull task, NSUInteger idx, BOOL * _Nonnull stop) {
        [task.subTaskArray enumerateObjectsUsingBlock:^(SegmentDownloadTask_SubTask * _Nonnull subTask, NSUInteger idx, BOOL * _Nonnull stop) {
            subTask.downloadTask = nil;
            subTask.isDownloading = NO;
        }];
    
    }];
    manager.downloaders = nil;
    manager.downloaders = [NSMutableArray new];
}
+(void)checkDownloadSubTaskDownloadTask{

    SegmentDownload *manager = [SegmentDownload share];
    [manager.downloadTaskArray enumerateObjectsUsingBlock:^(SegmentDownloadTask * _Nonnull task, NSUInteger idx, BOOL * _Nonnull stop) {
        if (task.fileTotalBytes != 0) {
            [task initDownloadSubTask];
            [task.subTaskArray enumerateObjectsUsingBlock:^(SegmentDownloadTask_SubTask * _Nonnull subTask, NSUInteger idx, BOOL * _Nonnull stop) {
                if (subTask.downloadTask == nil && subTask.isFinished == NO) {
                    subTask.downloadTask = [subTask getDownloadTask];
                }
            }];
        }
        else{
            [task getFileTotalLength];
        }
    }];
}

+(void)addMessageReceiver:(id<SegmentDownloadProtocol>)receiver{
    SegmentDownload *manager = [SegmentDownload share];
    if (![manager.receivers containsObject:receiver]) {
        [manager.receivers addObject:receiver];
    }
}
+(void)setMessageReceiverContainer:(id)container{
    SegmentDownload *manager = [SegmentDownload share];
    manager.containerReceiver  = container;
}
+(void)cleanMessageReceivers{
    SegmentDownload *manager = [SegmentDownload share];
    [manager.receivers removeAllObjects];
}
+(id<SegmentDownloadProtocol>)getReceiverWithDownloadTask:(SegmentDownloadTask *)task{
//    return nil;
    SegmentDownload *manager = [SegmentDownload share];
    __block id receiver = nil;
    id containerReceiver = manager.containerReceiver;
    if ([containerReceiver isKindOfClass:[UITableView class]]) {
        UITableView *table = (UITableView *)containerReceiver;
        [table.visibleCells enumerateObjectsUsingBlock:^(__kindof UITableViewCell<SegmentDownloadProtocol> * _Nonnull cell, NSUInteger idx, BOOL * _Nonnull stop) {
            if ([cell conformsToProtocol:@protocol(SegmentDownloadProtocol)]) {
                if ([cell getDownloadTask] == task) {
                    receiver = cell;
                    *stop = YES;
                }
            }
        }];
    }
    if (!receiver) {
        [manager.receivers enumerateObjectsUsingBlock:^(id<SegmentDownloadProtocol>  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            if ([obj getDownloadTask] == task) {
                receiver = obj;
                *stop = YES;
            }
        }];
    }
    

    return receiver;
}
+(NSString *)getArchiverPath{
    return [SegmentDownload filePathWithFileName:@"download.db"];
}

#pragma mark  添加下载任务（只添加任务，不启动下载）
+(SegmentDownloadTask *)addDownloadTaskWithURL:(NSURL *)URL totalBytes:(NSInteger)totalBytes{
    SegmentDownload *manager = [SegmentDownload share];
//    __block BOOL isExist = NO;
//    [manager.downloadTaskArray enumerateObjectsUsingBlock:^(SegmentDownloadTask * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
//        if ([obj.downloadURL isEqual:URL]) {
//            isExist = YES;
//        }
//    }];
//    if (isExist) {
//        return nil;
//    }
    SegmentDownloadTask *task = [[SegmentDownloadTask alloc]initWithURL:URL fileTotalBytes:totalBytes];
    [manager.downloadTaskArray addObject:task];
//    [manager.downloadTaskDic setObject:task forKey:task.downloadURL.absoluteString];
//    [SegmentDownload saveDownloadData];
    return task;
}
+(SegmentDownloadTask *)getDownloadTaskWithURL:(NSURL *)URL{
    __block SegmentDownloadTask *task = nil;
    SegmentDownload *manager = [SegmentDownload share];
    task = [manager.downloadTaskDic objectForKey:URL.absoluteString];
    [manager.downloadTaskArray enumerateObjectsUsingBlock:^(SegmentDownloadTask * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj.downloadURL isEqual:URL]) {
            task = obj;
            *stop = YES;
        }
    }];
    return task;
}
//////////////////////////////////////////////////////////////
#pragma mark 把任务状态强制修改为可下载
//////////////////////////////////////////////////////////////
+(void)prepareDownloading:(SegmentDownloadTask *)task{
    [SegmentDownload updateDownloadTask:task status:kDownloadTaskStatusWaiting];
//    [SegmentDownload downloaderStartWork];
}
//////////////////////////////////////////////////////////////
#pragma mark  开始下载
//////////////////////////////////////////////////////////////
+(void)startDownload{

    [SegmentDownload checkDownloadSubTaskDownloadTask];
    [SegmentDownload downloaderStartWork];
    
}
+(void)downloaderStartWork{
    if ([SegmentDownloadSession session] == nil) {
        //session 可能在重新创建
        return;
    }
    SegmentDownload *manager = [SegmentDownload share];
    BOOL unfindedDownloader = YES;
    SegmentDownloadTask *canDownloadTask = [SegmentDownload getCanDownloadTask];
    if (!canDownloadTask) {
        return;
    }
    SegmentDownloadTask_SubTask *canDownloadSubTask = [SegmentDownload getCanDownloadSubTask:canDownloadTask];
    if (canDownloadSubTask) {
        for (SegmentDownloader *downloader in manager.downloaders) {
            if (downloader.isWorking== NO) {
                [SegmentDownload updateDownloadTask:canDownloadTask status:kDownloadTaskStatusDownloading];
                [downloader startDownloadSubTask:canDownloadSubTask];
                unfindedDownloader = NO;
                [SegmentDownload downloaderStartWork];
                break;
            }
        }
        if (canDownloadSubTask && unfindedDownloader) {
            if (manager.downloaders.count < manager.maxConcurrent) {
                SegmentDownloader *downloader =[SegmentDownload getNewDownloader];
                [manager.downloaders addObject:downloader];
                [SegmentDownload downloaderStartWork];
                return;
            }
            
        }
    }
}
//////////////////////////////////////////////////////////////
#pragma mark  停止下载
//////////////////////////////////////////////////////////////
+(void)stopDownload{
    SegmentDownload * manager = [SegmentDownload share];
    
    [manager.downloaders enumerateObjectsUsingBlock:^(SegmentDownloader * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (obj.isWorking) {
            [obj stopDownlod];
        }
    }];
    
    [manager.downloadTaskArray removeAllObjects];
}
+(BOOL)allTaskFinished{
    __block BOOL isFinished = YES;
    SegmentDownload * manager = [SegmentDownload share];
    [manager.downloadTaskArray enumerateObjectsUsingBlock:^(SegmentDownloadTask * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (obj.status == kDownloadTaskStatusDownloading || obj.status == kDownloadTaskStatusWaiting) {
            isFinished = NO;
            *stop = YES;
        }
    }];
    
    return isFinished;
}
+(void)configMaxConcurrent:(NSInteger)maxConcurrent;
{
    if (maxConcurrent <= 0) {
        return;
    }
    SegmentDownload *manager = [SegmentDownload share];
    manager.maxConcurrent = maxConcurrent;

    
}
+(SegmentDownloader*)getNewDownloader{
    SegmentDownloader *downloader = [SegmentDownloader new];
    return downloader;
}

//////////////////////////////////////////////////////////////
#pragma mark 根据优先级获取下载任务
//////////////////////////////////////////////////////////////
+(SegmentDownloadTask *)getCanDownloadTask{
    SegmentDownload * manager = [SegmentDownload share];
    SegmentDownloadTask *canDownloadTask = nil;
    float maxPriority = -1;
    for (SegmentDownloadTask *task in manager.downloadTaskArray) {
        if (task.status == kDownloadTaskStatusWaiting || task.status == kDownloadTaskStatusDownloading) {
            if (task.priority > maxPriority) {
                maxPriority = task.priority;
                canDownloadTask = task;
            }
        }
    }
    if (![canDownloadTask canDownload]) {
        return nil;
    }
    if ([canDownloadTask checkDownloadTaskIsFinished]) {
        return [SegmentDownload getCanDownloadTask];
    }
    return canDownloadTask;
}

//////////////////////////////////////////////////////////////
#pragma mark 根据优先级获取下载任务
//////////////////////////////////////////////////////////////
+(SegmentDownloadTask_SubTask *)getCanDownloadSubTask:(SegmentDownloadTask *)downloadTask{
    __block SegmentDownloadTask_SubTask *canDownloadSubTask = nil;
    if (downloadTask.subTaskArray.count == 0) {
        [downloadTask initDownloadSubTask];
    }
    [downloadTask.subTaskArray enumerateObjectsUsingBlock:^(SegmentDownloadTask_SubTask * _Nonnull subTask, NSUInteger idx, BOOL * _Nonnull stop) {

        if ([subTask canDownload]) {
            canDownloadSubTask = subTask;
            *stop = YES;
        }
    }];
    if (canDownloadSubTask == nil) {
        NSLog(@"找不到子下载任务");
    }
    return canDownloadSubTask;
}

+(NSString *)filePathWithFileName:(NSString *)fileName{
    NSString *fullname = fileName;
    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    path = [path stringByAppendingPathComponent:fullname];
    return path;
}

#pragma mark -- Query Task
+(SegmentDownloadTask *)queryTaskWithTaskDescription:(NSString *)taskDescription{
    SegmentDownload *manager = [SegmentDownload share];
    NSString * taskUniqueIdentifier = [[taskDescription componentsSeparatedByString:@"_"]firstObject];
    SegmentDownloadTask *task = nil;
    for (SegmentDownloadTask *obj in manager.downloadTaskArray) {
        if ([obj.uniqueIdentifier isEqualToString:taskUniqueIdentifier]) {
            return  obj;
        }
    }
    return task;
}
+(void)queryTaskWithTaskDescription:(NSString *)taskDescription completion:(void(^)(SegmentDownloadTask *task,SegmentDownloader *downloader,SegmentDownloadTask_SubTask *subTask,NSInteger subTaskIndex))completion{
    SegmentDownload *manager = [SegmentDownload share];
    NSString * taskUniqueIdentifier = [[taskDescription componentsSeparatedByString:@"_*_*_"]firstObject];
//    SegmentDownloadTask *task = [manager.downloadTaskDic valueForKey:taskUniqueIdentifier];
    SegmentDownloadTask *task = manager.downloadTaskArray.firstObject;
    for (SegmentDownloadTask *obj in manager.downloadTaskArray) {
        if ([obj.uniqueIdentifier isEqualToString:taskUniqueIdentifier]) {
            task = obj;
            break;
        }
    }
    SegmentDownloadTask_SubTask *subTask = nil;
    NSInteger subTaskIndex = [[[taskDescription componentsSeparatedByString:@"_*_*_"]lastObject]integerValue];
    if (task.subTaskArray.count > subTaskIndex) {
         subTask = task.subTaskArray[subTaskIndex];
    }
    
    
    SegmentDownloader *_downloader = nil;
    
    for (SegmentDownloader *downloader in manager.downloaders) {
        if (downloader.downloadSubTask == subTask) {
            _downloader = downloader;
        }
    }
    
    if (completion) {
        completion(task,_downloader,subTask,subTaskIndex);
    }
    
}
+(void)updateDownloadTask:(SegmentDownloadTask *)downloadTask status:(DownloadTaskStatus)status{
    if (downloadTask.status != status) {
        downloadTask.status = status;
        [SegmentDownload saveDownloadData];
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            dispatch_async(dispatch_get_main_queue(), ^{
                id<SegmentDownloadProtocol>receiver = [SegmentDownload getReceiverWithDownloadTask:downloadTask];
                [receiver updateDownloadTaskState:status];
            });
        });
        
    }
}



@end
